/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020.
 * Description: support fiq_glue_api.c for x86
 * Author: yingelin <yingelin@huawei.com>
 * Create: 2020-10-21
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/printk.h>
#include "fiq_glue_private.h"

static char fiq_first_enter = FIQ_FIRST_REGISTER;
DEFINE_MUTEX(fiq_register_lock);

int request_fiq(void (*fiq_callback_func)(unsigned int), unsigned int fiq_num)
{
	int ret = 0;

	if (!fiq_priv.callback.fiq_init_handle) {
		pr_err("fiq_init_handle is NULL.\n");
		return -EINVAL;
	}
	if (!fiq_priv.callback.fiq_get_source) {
		pr_err("fiq_get_source is NULL.\n");
		return -EINVAL;
	}
	if (!fiq_priv.callback.fiq_cleansource_handle) {
		pr_err("fiq_cleansource_handle is NULL.\n");
		return -EINVAL;
	}

	if (!fiq_callback_func) {
		pr_err("[%s]fiq callback function is NULL.\n", __func__);
		return -EINVAL;
	}
	if (fiq_num != FIQ_NMI_NUM) {
		pr_err("[%s]fiq num invalid:%u.\n", __func__, fiq_num);
		return -EINVAL;
	}
	pr_devel("register fiq%u callback\n", fiq_num);
	mutex_lock(&fiq_register_lock);
	if (fiq_first_enter == FIQ_FIRST_REGISTER) {
		ret = fiq_first_register(fiq_callback_func, fiq_num);
		if (ret) {
			pr_err("[%s] fail to register fiq-no %u first func %pS\n",
			       __func__, fiq_num, fiq_callback_func);
			goto out;
		}
		pr_info("[%s] succeed to register fiq-no %u  first func %pS\n",
			__func__, fiq_num, fiq_callback_func);
		/* Make sure this branch only be executed once */
		fiq_first_enter = FIQ_ALREADY_REGISTER;
	} else {
		ret = fiq_more_register(fiq_callback_func, fiq_num);
		if (ret) {
			pr_err("[%s] fail to register fiq-no %u  more func %pS\n",
			       __func__, fiq_num, fiq_callback_func);
			goto out;
		}
		pr_info("[%s] succeed to register fiq-no %u more func %pS\n",
			__func__, fiq_num, fiq_callback_func);
	}
out:
	mutex_unlock(&fiq_register_lock);
	return ret;
}
EXPORT_SYMBOL(request_fiq);

int free_fiq(void (*fiq_callback_func)(unsigned int), unsigned int fiq_num)
{
	struct fiq_num_node *fiq_node = NULL;
	struct fiq_callback_node *callback_node = NULL;

	if (!fiq_callback_func) {
		pr_err("[%s]fiq callback function is NULL.\n", __func__);
		return -EINVAL;
	}

	mutex_lock(&fiq_register_lock);
	if (fiq_first_enter == FIQ_FIRST_REGISTER) {
		mutex_unlock(&fiq_register_lock);
		pr_warn("[%s]fiq callback nonregister.\n", __func__);
		return -EACCES;
	}

	fiq_node = fiq_find_node(fiq_num);
	if (!fiq_node) {
		pr_err("[%s]fiq register num %u is NULL.\n", __func__, fiq_num);
		goto error_inval;
	}

	callback_node = fiq_find_callback(fiq_node, fiq_callback_func);
	if (!callback_node) {
		pr_err("[%s] can not find func %pS\n",
		       __func__, fiq_callback_func);
		goto error_inval;
	}

	list_del(&callback_node->node);
	kfree(callback_node);

	mutex_unlock(&fiq_register_lock);
	pr_info("[%s] success to deregister fiq-no %u func %pS\n",
		__func__, fiq_num, fiq_callback_func);
	return 0;

error_inval:
	mutex_unlock(&fiq_register_lock);
	return -EINVAL;
}
EXPORT_SYMBOL(free_fiq);

int free_fiq_all(unsigned int fiq_num)
{
	struct fiq_num_node *fiq_node = NULL;

	mutex_lock(&fiq_register_lock);
	if (fiq_first_enter == FIQ_FIRST_REGISTER) {
		mutex_unlock(&fiq_register_lock);
		pr_warn("[%s]fiq callback nonregister.\n", __func__);
		return -EACCES;
	}

	fiq_node = fiq_find_node(fiq_num);
	if (!fiq_node) {
		pr_err("[%s]fiq register num %u is NULL.\n", __func__, fiq_num);
		goto error_inval;
	}

	fiq_free_callback(fiq_node);

	mutex_unlock(&fiq_register_lock);
	pr_info("[%s] succee to deregister all func of fiq-no %u\n",
		__func__, fiq_num);
	return 0;

error_inval:
	mutex_unlock(&fiq_register_lock);
	return -EINVAL;
}
EXPORT_SYMBOL(free_fiq_all);
